﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Path = System.IO.Path;
using System.Windows.Xps.Packaging;

namespace XPSConverter {
    /// <summary>
    /// Interaction logic for XPSConverterWindow.xaml
    /// Converts xps to images and paginates them - uses 
    /// http://craigwatson1962.wordpress.com/2010/11/26/convert-an-xps-to-jpeg-png-tiff-or-bmp-in-a4-pages/#comment-244
    /// </summary>
    public partial class XPSConverterWindow : Window {        

        public List<Tuple<string, Size>> PaperSizes {
            get {
                return new List<Tuple<string, Size>> {
                                                         new Tuple<string, Size>("A4_300dpi", new Size(2480, 3508)),
                                                         new Tuple<string, Size>("A3_300dpi", new Size(4962, 3507)),
                                                         new Tuple<string, Size>("A1_300dpi", new Size(9933, 7017)),
                                                         new Tuple<string, Size>("A0_300dpi", new Size(14043, 9933)),
                                                         new Tuple<string, Size>("A4_600dpi", new Size(2*2480, 2*3508)),
                                                         new Tuple<string, Size>("A3_600dpi", new Size(2*4962, 2*3507)),
                                                         new Tuple<string, Size>("A1_600dpi", new Size(2*9933, 2*7017)),
                                                         new Tuple<string, Size>("A0_600dpi", new Size(2*14043, 2*9933))
                                                     };
            }
        }

        public List<string> FileTypes {
            get {
                return new List<string> {
                                            "png",
                                            "jpg",
                                            "tiff",
                                            "gif",
                                            "bmp",
                                            "wdp"
                                        };
            }
        }

        public List<string> Papers = new List<string>{"Hello","Bob"};
        public XPSConverterWindow() {
            InitializeComponent();
        }

        private void OpenClick(object sender, RoutedEventArgs e) {
            Microsoft.Win32.OpenFileDialog dlg =
               new Microsoft.Win32.OpenFileDialog {
                   FileName = "LoadXPS",
                   Filter = "An XPS file (*.xps)|*.xps"
               };

            // Show open file dialog box
            bool? result = dlg.ShowDialog();

            if (result == true) {
                Tuple<string, Size> paper = (Tuple<string, Size>) this.PaperSize.SelectedItem;
                var pageImages = SaveXpsPageToBitmap(dlg.FileName,this.PaperSize.SelectedItem as string,
                    paper.Item2,
                    paper.Item1);                
            }

        }

        public static List<string> SaveXpsPageToBitmap(string xpsFileName, string extension, Size size, string paperName) {
            List<string> fileNames = new List<string>();

            using (XpsDocument xpsDoc = new XpsDocument(xpsFileName, FileAccess.Read)) {
                FixedDocumentSequence docSeq = xpsDoc.GetFixedDocumentSequence();
                // You can get the total page count from docSeq.PageCount
                for (int pageNum = 0; pageNum < docSeq.DocumentPaginator.PageCount; ++pageNum) {
                    using (DocumentPage docPage = docSeq.DocumentPaginator.GetPage(pageNum)) {                       
                        RenderTargetBitmap renderTarget =
                            new RenderTargetBitmap((int)docPage.Size.Width,
                                                    (int)docPage.Size.Height,
                                                    96, // WPF (Avalon) units are 96dpi based
                                                    96,
                                                    PixelFormats.Default);
                        renderTarget.Render(docPage.Visual);
                        BitmapEncoder encoder;
                        switch (extension) {
                            case "png": encoder = new PngBitmapEncoder(); break;
                            case "jpg": encoder = new JpegBitmapEncoder(); break;
                            case "tiff": encoder = new TiffBitmapEncoder(); break;
                            case "gif": encoder = new GifBitmapEncoder(); break;
                            case "bmp": encoder = new BmpBitmapEncoder(); break;
                            case "wdp": encoder = new WmpBitmapEncoder(); break;
                            default: extension = "png"; encoder = new PngBitmapEncoder(); break;
                        }

                        encoder.Frames.Add(BitmapFrame.Create(renderTarget));
                        string fileName = Path.GetDirectoryName(xpsFileName)+"\\"+Path.GetFileNameWithoutExtension(xpsFileName) + "_Page" + pageNum + "." + extension;
                        fileNames.Add(fileName);
                        using (FileStream pageOutStream =
                            new FileStream(fileName, FileMode.Create, FileAccess.Write)) {
                            encoder.Save(pageOutStream);
                            pageOutStream.Close();
                        }

                        GeneratePagesAsFiles(fileName, Path.GetFileNameWithoutExtension(fileName),
                                             renderTarget,
                                             size,paperName,extension);
                    }
                }
                xpsDoc.Close();
            }            
            return fileNames;
        }

        /// <summary>
        /// Produces a series of graphics files in the format specified from the
        /// input XPS file.
        /// </summary>
        /// <param name="originalFile">Used to generate the output file name
        /// fileNamePart"&gt;The filename without directory or extensions,
        /// which is used to generate the output file
        /// pageNum"&gt;Used to generate the output file name
        /// renderTarget"&gt;The bit map representation of the page to
        /// be tiled into multiple parts</param>
        /// <param name="fileNamePart">The file name part.</param>
        /// <param name="renderTarget">The render target.</param>
        /// <param name="paper">The size of the tiles (in pixels - image is 96 dpi)
        /// of the image that will be created.
        /// PaperType"&gt;A string which describes the tile size (e.g. A3).
        /// Used in creation of the output file name</param>
        /// <param name="paperType">Type of the paper.</param>
        /// <param name="extension">The type of graphics file created.
        /// Used to determine the encoder used, and to create the metadata for the file.</param>
        private static void GeneratePagesAsFiles(string originalFile, string fileNamePart, BitmapSource renderTarget,
            Size paper, string paperType, string extension) {

            int iHrozTiles = ((int)(renderTarget.Width / paper.Width)) + 1;
            int iVertTiles = ((int)(renderTarget.Height / paper.Height)) + 1;
            BitmapEncoder encoder;
            int page = 1;
            for (int i = 0; i < iHrozTiles; i++) {
                for (int j = 0; j < iVertTiles; j++) {
                    string outputFileName =
                    Path.GetDirectoryName(originalFile) + "\\" + fileNamePart + "_" + paperType + "_page_ " + page + "_" + i + "_" + j + "." + extension;
                    page++;
                    if (File.Exists(outputFileName))
                        continue;
                    Int32Rect crop = new Int32Rect(
                        (int)Math.Min(i * paper.Width, renderTarget.Width),
                        (int)Math.Min(j * paper.Height, renderTarget.Height),
                        (int)Math.Min(paper.Width, renderTarget.Width - ((i) * paper.Width)),
                        (int)Math.Min(paper.Height, renderTarget.Height - (j * paper.Height)));
                    if (crop.X == renderTarget.Width || crop.Y == renderTarget.Height)
                        continue;                    
                    CroppedBitmap cb1 = new CroppedBitmap(renderTarget, crop);
                    BitmapMetadata metadata =
                        MakeMetadata(extension, fileNamePart, i, j, iHrozTiles, iVertTiles);
                    switch (extension) {
                        case "png": encoder = new PngBitmapEncoder(); break;
                        case "jpg": encoder = new JpegBitmapEncoder(); break;
                        case "tiff": encoder = new TiffBitmapEncoder(); break;
                        case "gif": encoder = new GifBitmapEncoder(); break;
                        case "bmp": encoder = new BmpBitmapEncoder(); break;
                        case "wdp": encoder = new WmpBitmapEncoder(); break;
                        default: extension = "png"; encoder = new PngBitmapEncoder(); break;
                    }
                    encoder.Frames.Add(BitmapFrame.Create(cb1, null, metadata, null));
                    using (FileStream pageOutStream =
                        new FileStream(outputFileName, FileMode.Create, FileAccess.Write)) {
                        encoder.Save(pageOutStream);
                        pageOutStream.Close();
                    }
                }
            }
        }
        
        private static BitmapMetadata MakeMetadata(string extension, string type, int i, int i1, int iHrozTiles, int iVertTiles) {
            return new BitmapMetadata(extension);
        }
    }
}
